package com.hans.scala.ImplicitConversion

import java.util.Comparator

import scala.math.sqrt


/**
  * 上下界
  */
object UpperAndLowerBound {

  class Animal(val age: Int) extends Comparable[Animal] {
    override def compareTo(p: Animal): Int = {
      age - p.age
    }

    override def toString: String = "[Animal:" + age.toString + "]"
  }

  class Person(val name: String, age: Int) extends Animal(age) {
    override def toString: String = "[Person:" + name.toString + "," + age.toString + "]"
  }

  //T是Comparable的子类
  class Pair[T <: Comparable[T]](val first: T, val second: T) {
    def smaller: T = if (first.compareTo(second) < 0) first else second

    //    def replaceFirst(newFirst: T) = new Pair[T](newFirst, second)
    // R是T的父类 下界
    def replaceFirst[R >: T](newFirst: T) = new Pair[T](newFirst, second)

    override def toString: String = "[pair:" + first.toString + "," + second.toString + "]"
  }

  def main(args: Array[String]) {
    //上界
    val p1 = new Pair("b", "a")
    println(p1.second.getClass)
    println(p1.smaller)
    //Error:(17, 13) inferred type arguments [Int] do not conform to class Pair's type parameter bounds [T <: Comparable[T]]
    //    println(new Pair(6, 3).smaller)

    val p2 = new Pair[Animal](new Animal(3), new Person("hans", 2))
    println(p2.toString)
    val p3 = p2.replaceFirst(new Person("joyce", 1))
    println(p3.toString)
    val p4 = p2.replaceFirst(new Animal(5))
    println(p4.toString)

    val p5 = new Pair[Animal](new Person("hans", 2), new Person("joyce", 2))
    val p6 = p2.replaceFirst(new Animal(5))
    println(p6)

  }
}

/**
  * 视图界定
  */
object ViewDefinition {

  def main(args: Array[String]) {
    val p = new Pair(3, 5) //int 被隐式转换成RichInt
    println(p.second.getClass)
    println(p.smaller)
  }

  class Pair[T](val first: T, val second: T)(implicit cp: T => Comparable[T]) {
    def smaller: T = if ((second compareTo first) < 0) first else second
  }

  //  class Pair[T <% Comparable[T]](val first: T, val second: T) {
  //    def smaller = if ((second compareTo first) < 0) first else second
  //  }

  //  class Pair[T1](val first: T1, val second: T1)(implicit a: T1 => Ordered[T1]) {
  //    def smaller: T1 = if (first < second) first else second
  //  }
}

/**
  * 上下文界定
  */
object ContextBounded {

  def main(args: Array[String]) {
    val p = new Pair(3, 5)
    println(p.smaller)
  }

  class Pair[T: Ordering](val first: T, val second: T) {
    def smaller(implicit ord: Ordering[T]) = if (ord.compare(first, second) < 0) first else second

    //        def smaller(implicit ord: Ordering[T]) = if (first < second) first else second

    override def toString: String = "[pair:" + first.toString + "," + second.toString + "]"
  }

}

object ContextBounded2 {
  def main(args: Array[String]) {
    implicit val c = new Comparator[Int] {
      override def compare(a: Int, b: Int) = a - b
    }
    println(max(3, 2))
    println(max1(3, 2))
    println(max2(3, 2))
  }

  def max[T](a: T, b: T)(implicit cp: Comparator[T]) = {
    if (cp.compare(a, b) > 0) a else b
  }

  //  T : Comparator 就表示存在一个 Comparator[T]类型的隐式值。
  def max1[T: Comparator](a: T, b: T) = {
    def inner(implicit c: Comparator[T]) = c.compare(a, b)

    if (inner > 0) a else b
  }

  def max2[T: Comparator](a: T, b: T) = {
    // implicitly是在Predef.scala里定义的，
    // 它是一个特殊的方法，编译器会记录当前上下文里的隐式值，而这个方法则可以获得某种类型的隐式值。
    val cp = implicitly[Comparator[T]]
    if (cp.compare(a, b) > 0) a else b
  }
}

/**
  * Manifest上下文界定
  */
object ManifestContextBounded {

  case class Foo[A](a: A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    //    def getStringLength(implicit evidence: A =:= String) = a.length
    def getStringLength(implicit evidence: =:=[A, String]) = a.length
  }

  def main(args: Array[String]) {
    println(makePair(3, 5).getClass)
    println(Foo("blah").getStringLength)
    println(Foo(1))
    test("3")
    test(3)
    foo2(List(1, 2))
    foo2(List("1", "2"))

    import scala.reflect.runtime.universe._
    def paramInfo[T: TypeTag](x: T): Unit = {
      val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
      println(s"type of $x has type arguments $targs")
    }
    paramInfo(Array(1))
  }

  def test[T: Manifest](a: T) {
    println(a)
  }

  def foo[T](x: List[T])(implicit m: Manifest[T]) = {
//    if (T <:< String){}
//    if (m <:< reflect.classTag[String]){}
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  def foo2[T: Manifest](x: List[T]) = {
    if (manifest[T] <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  //根据T实例化Manifest
  def makePair[T: Manifest](first: T, second: T): Array[T] = {
    val r = new Array[T](2)
    r(0) = first
    r(1) = second
    r
  }
}

object ManifestContextBounded2 {
  def main(args: Array[String]) {
    foo(List("one", "two")) // Hey, this list is full of strings
    foo(List(1, 2)) // Non-stringy list
    foo(List("one", 2)) // Non-stringy list
    foo1(List("one", 2)) // Non-stringy list
    foo2(List("one", "two")) // Hey, this list is full of strings
  }

  def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  //利用上下文界定
  def foo1[T: Manifest](x: List[T]) = {
    def inner(implicit m: Manifest[T]) = m <:< manifest[String]

    if (inner)
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  def foo2[T: Manifest](x: List[T]) = {
    val m = implicitly[Manifest[T]]
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }
}

object CoContravariant {

  class Animal(val age: Int) {

    override def toString: String = "[Animal:" + age.toString + "]"
  }

  class Person(val name: String, age: Int) extends Animal(age) {
    override def toString: String = "[Person:" + name.toString + "," + age.toString + "]"
  }

  class Pair[+T](val first: T, val second: T) {
    // R是T的父类 下界
    def replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second)

    //covariant type occurs in contravariant position
    //    def replaceFirst1[R <: T](newFirst: R): Unit = {
    //            first = newFirst
    //    }
    override def toString: String = "[pair:" + first.toString + "," + second.toString + "]"
  }

  def main(args: Array[String]) {
    sqrt(1.2d)
    val p2 = new Pair[Animal](new Person("hans", 2), new Animal(3))
    println(p2.toString)
    val p3 = p2.replaceFirst("joyce")
    println(p3.toString)

    val p4 = p2.replaceFirst(new Animal(5))
    println(p4.toString)

  }
}
